﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BlackScholesLib
{
    public static class BlackScholes_v1
    {
        private static Func<double, double> NormSDist = NormSDist_v4.NormSDist;
        private static Func<double, double> LittleN = LittleN_v1.Calculate;
        
        public static double Calculate(
            double spotPrice,
            double strike,
            double time,
            double rate,
            double dividend,
            double vol,
            string callPut,
            int reg0Fut1 = 0,
            int output = 0)
        {
            // Arguments
	        //Output - Price = 0, Delta = 1, Gamma = 2

            double volrootime;
            double d1;
            double d2;
            double discF;
            double divF;
            double optprice;
            double topline1;
            double topline2;
            double topline;
            double price;
            double delta;
            double gamma;

            discF = Math.Exp(-(rate*time));
            divF = Math.Exp(-(dividend*time));
            volrootime = Math.Pow(time, 0.5)*vol;

            //' Reg0Fut1 function distinguishes between regular options (0) and options on futures (1)

            if (reg0Fut1 == 0)
            {
                topline1 = Math.Log(spotPrice/strike);
                topline2 = ((rate - dividend) + (Math.Pow(vol, 2)/2))*time;
                topline = topline1 + topline2;
                d1 = topline/volrootime;
                d2 = d1 - volrootime;

                if (callPut == "C")
                {
                    price = (spotPrice*divF*NormSDist(d1)) - (strike*discF*NormSDist(d2));
                    delta = divF*NormSDist(d1);
                    gamma = (LittleN(d1) * divF) / (spotPrice * volrootime);
                }
                else
                {
                    price = strike*discF*NormSDist(-d2) - spotPrice*divF*NormSDist(-d1);
                    delta = divF*NormSDist(d1 - 1);
                    gamma = (LittleN(d1)*divF)/(spotPrice*volrootime);
                }
            }
            else
            {
                topline1 = Math.Log(spotPrice/strike);
                topline2 = (Math.Pow(vol, 2)/2)*time;
                topline = topline1 + topline2;
                d1 = topline/volrootime;
                d2 = d1 - volrootime;
                
                if (callPut == "C")
                {
                    price = discF * ((spotPrice * NormSDist(d1)) - (strike * NormSDist(d2)));
                    delta = discF * NormSDist(d1);
                    gamma = (LittleN(d1) * discF) / (spotPrice * volrootime);
                }
                else
                {
                    price = discF * ((strike * NormSDist(-d2)) - (spotPrice * NormSDist(-d1)));
                    delta = discF * (NormSDist(d1) - 1);
                    gamma = (LittleN(d1) * discF) / (spotPrice * volrootime);
                }                
            }
            
            switch(output)
            {
                case 0: // price
                    return price;
                case 1: // delta
                    return delta;
                case 2: // gamma
                    return gamma;
                default:
                    throw new ArgumentException(string.Format("unhandled output type {0}", output));
            }

        }
    }
}
